/*
* Copyright (c) 2012 Kyle Hayward
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "Precompiled.h"

#include "Context.h"
#include "D3DBuffer.h"

using namespace System;

namespace OptixDotNet
{
	D3DBuffer::D3DBuffer( Context^ context, D3DBufferDesc desc ) : Buffer( context )
	{
		Create( desc );
	}

	void D3DBuffer::Create( D3DBufferDesc% desc )
	{
		mApi = desc.Api;
		mFormat = desc.Format;
		mType = desc.Type;
		mWidth = desc.Width;
		mHeight = desc.Height;
		mDepth = desc.Depth;

		if( desc.Format == OptixDotNet::Format::Unknown )
			throw gcnew Exception( "D3DBuffer Error: Unknown buffer types not supported" );

		if( desc.Format == OptixDotNet::Format::User && desc.ElemSize <= 0 )
			throw gcnew Exception( "D3DBuffer Error: ElemSize must be non-zero for User formats." );

		if( desc.Type == BufferType::InputOutputLocal )
			throw gcnew Exception( "D3DBuffer Error: BufferType::InputOutputLocal Unsupported!" );

		unsigned int type = static_cast<unsigned int>( desc.Type );

		pin_ptr<RTbuffer> ptr = &mBuffer;
		if( mApi == D3DApi::Direct3D9 )
		{
			if( mContext->D3D9Device == IntPtr::Zero )
				throw gcnew Exception( "D3DBuffer Error: A valid Direct3D9Device must be set on the Optix Context before creating Direct3D9 interop resources." );

			CheckError( rtBufferCreateFromD3D9Resource( mContext->InternalPtr(), type, static_cast<IDirect3DResource9*>( desc.Resource.ToPointer() ), ptr ) );
		}
		else if( mApi == D3DApi::Direct3D10 )
		{
			if( mContext->D3D10Device == IntPtr::Zero )
				throw gcnew Exception( "D3DBuffer Error: A valid D3D10Device must be set on the Optix Context before creating Direct3D10 interop resources." );

			CheckError( rtBufferCreateFromD3D10Resource( mContext->InternalPtr(), type, static_cast<ID3D10Resource*>( desc.Resource.ToPointer() ), ptr ) );
		}
		else if( mApi == D3DApi::Direct3D11 )
		{
			if( mContext->D3D11Device == IntPtr::Zero )
				throw gcnew Exception( "D3DBuffer Error: A valid D3D11Device must be set on the Optix Context before creating Direct3D11 interop resources." );

			CheckError( rtBufferCreateFromD3D11Resource( mContext->InternalPtr(), type, static_cast<ID3D11Resource*>( desc.Resource.ToPointer() ), ptr ) );
		}
		else
		{
			throw gcnew Exception( "D3DBuffer Error: Unsupported D3D API." );
		}

		CheckError( rtBufferSetFormat( mBuffer, static_cast<RTformat>( desc.Format ) ) );

		if( desc.Format == OptixDotNet::Format::User )
		{
			CheckError( rtBufferSetElementSize( mBuffer, desc.ElemSize ) );
		}

		SetSize( desc.Width, desc.Height, desc.Depth );
	}

	void D3DBuffer::Register()
	{
		if( mApi == D3DApi::Direct3D9 )
		{
			CheckError( rtBufferD3D9Register( mBuffer ) );
		}
		else if( mApi == D3DApi::Direct3D10 )
		{
			CheckError( rtBufferD3D10Register( mBuffer ) );
		}
		else if( mApi == D3DApi::Direct3D11 )
		{
			CheckError( rtBufferD3D11Register( mBuffer ) );
		}
	}

	void D3DBuffer::Unregister()
	{
		if( mApi == D3DApi::Direct3D9 )
		{
			CheckError( rtBufferD3D9Unregister( mBuffer ) );
		}
		else if( mApi == D3DApi::Direct3D10 )
		{
			CheckError( rtBufferD3D10Unregister( mBuffer ) );
		}
		else if( mApi == D3DApi::Direct3D11 )
		{
			CheckError( rtBufferD3D11Unregister( mBuffer ) );
		}
	}
	
	IntPtr D3DBuffer::Resource::get()
	{
		if( mApi == D3DApi::Direct3D9 )
		{
			IDirect3DResource9* resource;
			CheckError( rtBufferGetD3D9Resource( mBuffer, &resource ) );
			return IntPtr( resource );
		}
		else if( mApi == D3DApi::Direct3D10 )
		{
			ID3D10Resource* resource;
			CheckError( rtBufferGetD3D10Resource( mBuffer, &resource ) );
			return IntPtr( resource );
		}
		else if( mApi == D3DApi::Direct3D11 )
		{
			ID3D11Resource* resource;
			CheckError( rtBufferGetD3D11Resource( mBuffer, &resource ) );
			return IntPtr( resource );
		}

		return IntPtr::Zero;
	}
}